終於跑完一半鐵人賽了,接下來會慢慢深入JavaScript比較進階的部分(嚇怕。這兩三天會進入原型的課題,而今天就先整理一下原型的基本概念。
我們可以把原型想像成一個藍圖。
例如我們想建一台車,就會想到一台車會包括以下的特性:
這就是一台車的藍圖,但它只是一個藍圖,並不是實實在在的一台車。接下來我們要實際建出一台車,就可以按這份藍圖去建起來,例如:
上圖可見我們建了兩台車,同樣是按車的藍圖去做出來,這裏反映出一些概念:
上文提及我們用車子的藍圖,產生出一台實際的車。在JavaScript裏面,我們可以用「函式建構子」來代表那個藍圖,再用new
的運算子,產生出一個實體物件(myCar、yourCar),如以下的程式碼:
//建立函式建構子
function car(type,color,person){
this.type = type;
this.color = color;
this.person = person;
}
//之後用函式建構子來產生實體物件
var myCar = new car('電動車', '紅色',4)
var yourCar = new car('貨車','藍色',2)
針對這個例子,在討論myCar
和yourCar
之前,我們先看看函式建構子car
有什麼特別,我們可以用console.dir(car)
去找出這個原型:
上圖可見,函式建構子裏面有一些屬性:
prototype
屬性prototype
屬性裏面,再有2個屬性:
constructor
屬性__proto__
屬性constructor
屬性,就是指回自己這個函式建構子的本身,即是car
這個函式。__proto__
屬性是什麼呢?就是這個函式再上一層的原型,就是object
,因為function
是屬於object
型別。
那麼兩個由同一個函式建構子產生的實體物件呢?我們用console.log(myCar)
和console.log(yourCar)
查一下:
把__proto__
打開,會發現它們都有同一個constructor
屬性,這不意外,因為它們都是由同一個函式建構子產生的。
我們可以總結一下實體物件的特別之處:
__proto__
,裏面有:
constructor
屬性__proto__
屬性剛才已經解釋過第一個了,那麼第二個屬性__proto__
呢?明明已經有了__proto__
,__proto__
裏面還要包多一個__proto__
,到底是什麼意思呢?這裏的__proto__
是指向它的原型,就是car
的prototype
。
拿出裏面__proto__
與car
函式建構子的prototype
屬性比對一下來證明:
聰明的你會發覺這些東西就是一個連著一個,沒錯,這個關係就是原型鏈的概念。聽起來很複雜?我們可以把整個原型鏈關係想像成下圖這樣:
看到這裏,你可能會問為什麼要學這樣煩的關係?因為我們可以透過修改原型,來產生更多有一堆相同屬性的實體物件。
就如剛才提及車的例子,試想想我要建100台車,然後裏面都是重複有那3個屬性,我就是重複寫100次,吃掉很多記憶體。但是,如果那100台車都是在共用同一個原型,那個原型裏已經寫好那3個屬性,那只會佔很少記憶體。例如以下例子,我想要增加drive
這個方法:
//在函式建構子的prototype裏新增drive的方法
car.prototype.drive = function(){
console.log(`我可以駕駛${this.type}`)
}
//myCar本身沒有drive方法,但JavaScript會找尋上一層原型,找到drive方法
myCar.drive() //我可以駕駛電動車
yourCar.drive() //我可以駕駛貨車
以上可見,我只要在car
的prototype
加上drive
方法,之後那些物件就能繼承去用那些方法。
我們這時候再查一下myCar
和yourCar
:
以上就更清晰可以見到,myCar
和yourCar
的原型都被加入了drive
方法。注意,如果我們之後想修改原型的屬性或方法,最好的做法就是回到建構子去改,並不是任意找一個物件,再指定它的原型去改,這樣會使後續維護更麻煩更易出錯。
JS 原力覺醒 Day21 - 原型
重新認識 JavaScript: Day 24 物件與原型鏈
JavaScript 核心篇 (六角學院)